package com.suse.common.util.date;

import com.suse.common.util.stream.DataStream;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * <p>
 * 日期时间工具类
 * </p>
 *
 * @author 辜玉彬
 * @since 2022-08-18 23:29
 */
public class DateUtil {

    public static Date now() {
        return new Date();
    }

    /**
     * LocalDateTime 对象 转为 Date 对象
     * @param localDateTime LocalDateTime 对象
     * @return Date 对象
     */
    public static Date date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date of(LocalDate localDate) {
        return of(localDate.atStartOfDay());
    }

    public static Date of(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date of(int year,int month, int dayOfMonth) {
        LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
        return of(localDate);
    }

    public static Date ofEpochDay(long epochDay) {
        LocalDate localDate = LocalDate.ofEpochDay(epochDay);
        return of(localDate);
    }

    public static long toEpochDay(Date date) {
        return localDate(date).toEpochDay();
    }

    /**
     * Date 对象 转为 LocalDate 对象
     * @param date Date 对象
     * @return LocalDate 对象
     */
    public static LocalDate localDate(Date date) {
        Instant instant = date.toInstant();
        return instant.atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDateTime localDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * Date 数组 转为 LocalDate 数组
     * @param dates Date 数组
     * @return LocalDate 数组
     */
    public static LocalDate[] localDates(Date[] dates) {
        LocalDate[] localDates = new LocalDate[dates.length];
        for (int i = 0; i < dates.length; i++) {
            localDates[i] = localDate(dates[i]);
        }
        return localDates;
    }

    /**
     * 得到指定日期对应的一周的日期数组
     * @param date 指定日期
     * @return 一周的日期数组
     */
    public static LocalDate[] weeks(LocalDate date) {
        LocalDate[] weeks = new LocalDate[7];
        LocalDate beginOfWeek = beginOfWeek(date);
        int week = 0;
        while(week < 7) {
            weeks[week] = beginOfWeek;
            beginOfWeek = beginOfWeek.plusDays(1);
            week++;
        }
        return weeks;
    }

    /**
     * 得到指定日期对应的一周的开始日期
     * @param date 指定日期
     * @return 一周的开始日期
     */
    public static LocalDate beginOfWeek(LocalDate date) {
        int week = date.getDayOfWeek().getValue();
        if (week == 1) {
            return date;
        }
        if (week == 2) {
            return date.minusDays(1);
        }
        if (week == 3) {
            return date.minusDays(2);
        }
        if (week == 4) {
            return date.minusDays(3);
        }
        if (week == 5) {
            return date.minusDays(4);
        }
        if (week == 6) {
            return date.minusDays(5);
        }
        if (week == 7) {
            return date.minusDays(6);
        }
        return date;
    }

    /**
     * 得到指定日期对应的一周的结束日期
     * @param date 指定日期
     * @return 一周的结束日期
     */
    public static LocalDate endOfWeek(LocalDate date) {
        LocalDate localDate = beginOfWeek(date);
        return localDate.plusDays(6);
    }

    /**
     * 开始日期到结束日期
     * @param start 开始日期
     * @param end 结束日期
     * @return 数据流
     */
    public static DataStream<LocalDate> datesUntil(LocalDate start, LocalDate end) {
        List<LocalDate> localDateList = new ArrayList<>();
        while(start.isBefore(end)) {
            localDateList.add(start);
            start = start.plusDays(1);
        }
        localDateList.add(end);
        return new DataStream<>(localDateList);
    }

    public static Stream<Date> datesUntil(Date start, Date end) {
        long endEpochDay = toEpochDay(start);
        long startEpochDay = toEpochDay(end);
        if (endEpochDay < startEpochDay) {
            throw new IllegalArgumentException(end + " < " + start);
        }
        return LongStream.range(endEpochDay, startEpochDay).mapToObj(DateUtil::ofEpochDay);
    }

    public static long until(Date start,Date end,TemporalUnit temporalUnit) {
        return localDateTime(start).until(localDateTime(end), temporalUnit);
    }

}
